﻿using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;

namespace HPSocket
{
    public class SocketAsyncEventArgsPool
    {
        private ConcurrentQueue<SocketAsyncEventArgs> _reuseQueue;
        private ConcurrentDictionary<int, SocketAsyncEventArgs> _usingArgsDicts;

        private int _initialCapacity;
        private int _maxCapacity;
        private int _currentNum;

        public int InitialCapacity { get => _initialCapacity; }
        public int MaxCapacity { get => _maxCapacity; }
        public int CurrentNum { get => _currentNum; }
        public int UsableNumber { get => (_maxCapacity - _currentNum + _reuseQueue.Count); }

        public SocketAsyncEventArgsPool(int initialCapacity, int maxCapacity)
        {
            _initialCapacity = initialCapacity;
            _maxCapacity = maxCapacity;
            _usingArgsDicts = new ConcurrentDictionary<int, SocketAsyncEventArgs>(5, maxCapacity);
            if (initialCapacity > 0)
            {
                var initDatas = new List<SocketAsyncEventArgs>(initialCapacity);
                _reuseQueue = new ConcurrentQueue<SocketAsyncEventArgs>(initDatas);
            }
            else
            {
                _reuseQueue = new ConcurrentQueue<SocketAsyncEventArgs>();
            }
        }

        public SocketAsyncEventArgs Rent(int bufferCapacity = 1024)
        {
            if (UsableNumber <= 0)
                return null;

            SocketAsyncEventArgs args = null;
            if (_reuseQueue.Count > 0 && _reuseQueue.TryDequeue(out args))
            {
                if (args.MemoryBuffer.Length < bufferCapacity)
                    args.SetBuffer(new byte[bufferCapacity]);
            }
            if (args == null)
            {
                args = new SocketAsyncEventArgs();
                args.SetBuffer(new byte[bufferCapacity]);
            }
            Interlocked.Increment(ref _currentNum);

            args.DisconnectReuseSocket = true;
            return args;
        }

        public void Return(SocketAsyncEventArgs args)
        {
            args.AcceptSocket?.Dispose();
            args.AcceptSocket = null;
            _reuseQueue.Enqueue(args);
            Interlocked.Decrement(ref _currentNum);
        }
    }

    //{
    //    private static ArrayPool<byte> ArrayPool = ArrayPool<byte>.Shared;

    //    public static SocketAsyncEventArgs Rent(int bufferSize)
    //    {
    //        // byte[] buffer = ArrayPool.Rent(bufferSize);
    //        // if (buffer == null || buffer.Length == 0)
    //        //     return null;
    //        SocketAsyncEventArgs args = new SocketAsyncEventArgs();
    //        args.SetBuffer(new byte[bufferSize]);
    //        return args;
    //    }

    //    public static void Return(SocketAsyncEventArgs args)
    //    {
    //        args.Dispose();
    //    }
    //}
}